﻿using System;
using System.Collections.Generic;

namespace TierraReborn
{
    public class Cpu
    {
        public int ax; // address register
        public int bx; // address register
        public int cx; // numerical register
        public int dx; // numerical register
        public char fl; // flag
        //public char sp; // stack pointer
        public Stack<int> st = new Stack<int>(MaxStackCount); // stack
        public int ip; // instruction pointer

        private const int MaxStackCount = 10;

        public Cpu()
        {
            ax = 0; // address register
            bx = 0; // address register
            cx = 0; // numerical register
            dx = 0; // numerical register
            fl = (char)0; // flag
            //sp = (char)0; // stack pointer
            //st = new Stack<int>(10); // stack
            ip = 0; // instruction pointer
        }
        
        public bool Execute(MachineInstruction.InstructionEnum instruction, Creature creature)
        {
            switch (instruction)
            {
                case MachineInstruction.InstructionEnum.nop_0:
                    return nop_0(creature);
                case MachineInstruction.InstructionEnum.nop_1:
                    return nop_1(creature);
                case MachineInstruction.InstructionEnum.or1:
                    return or1(creature);
                case MachineInstruction.InstructionEnum.sh1:
                    return sh1(creature);
                case MachineInstruction.InstructionEnum.zero:
                    return zero(creature);
                case MachineInstruction.InstructionEnum.if_cz:
                    return if_cz(creature);
                case MachineInstruction.InstructionEnum.sub_ab:
                    return sub_ab(creature);
                case MachineInstruction.InstructionEnum.sub_ac:
                    return sub_ac(creature);
                case MachineInstruction.InstructionEnum.inc_a:
                    return inc_a(creature);
                case MachineInstruction.InstructionEnum.inc_b:
                    return inc_b(creature);
                case MachineInstruction.InstructionEnum.dec_c:
                    return dec_c(creature);
                case MachineInstruction.InstructionEnum.inc_c:
                    return inc_c(creature);
                case MachineInstruction.InstructionEnum.push_ax:
                    return push_ax(creature);
                case MachineInstruction.InstructionEnum.push_bx:
                    return push_bx(creature);
                case MachineInstruction.InstructionEnum.push_cx:
                    return push_cx(creature);
                case MachineInstruction.InstructionEnum.push_dx:
                    return push_dx(creature);
                case MachineInstruction.InstructionEnum.pop_ax:
                    return pop_ax(creature);
                case MachineInstruction.InstructionEnum.pop_bx:
                    return pop_bx(creature);
                case MachineInstruction.InstructionEnum.pop_cx:
                    return pop_cx(creature);
                case MachineInstruction.InstructionEnum.pop_dx:
                    return pop_dx(creature);
                case MachineInstruction.InstructionEnum.jmp:
                    return jmp(creature);
                case MachineInstruction.InstructionEnum.jmpb:
                    return jmpb(creature);
                case MachineInstruction.InstructionEnum.call:
                    return call(creature);
                case MachineInstruction.InstructionEnum.ret:
                    return ret(creature);
                case MachineInstruction.InstructionEnum.mov_cd:
                    return mov_cd(creature);
                case MachineInstruction.InstructionEnum.mov_ab:
                    return mov_ab(creature);
                case MachineInstruction.InstructionEnum.move_iab:
                    return move_iab(creature);
                case MachineInstruction.InstructionEnum.adr:
                    return adr(creature);
                case MachineInstruction.InstructionEnum.adrb:
                    return adrb(creature);
                case MachineInstruction.InstructionEnum.adrf:
                    return adrf(creature);
                case MachineInstruction.InstructionEnum.mal:
                    return mal(creature);
                case MachineInstruction.InstructionEnum.divide:
                    return divide(creature);
                default:
                    throw new NotSupportedException();
            }
        }

        private bool divide(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool mal(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool adrf(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool adrb(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool adr(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool move_iab(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool mov_ab(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool mov_cd(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool ret(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool call(Creature creature)
        {
            throw new NotImplementedException();
        }

        private bool jmpb(Creature creature)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// /* move ip to template */
        /// 1. read the template (= combination of Nop0/Nop1 instructions following the jmp)
        /// 2. find the first complement template ascending from jmp (template = 0001 => complement = 1110)
        /// 3. set ip to instruction after template 
        /// 3b. (could also be at the beginning of the template, but this would make jmp calls a lot less efficient; BUT maybe more realistic, as this is a heavy operation)
        /// </summary>
        private bool jmp(Creature creature)
        {
            var soup = Soup.GetSingleton();

            // Read template
            var complementTemplate = new List<MachineInstruction.InstructionEnum>();
            for(int i=ip+1; ;i++)
            {
                if (i == soup.GetMax())
                {
                    ip++;
                    return false;
                }

                var instruction = soup.Get(i);
                if (instruction == MachineInstruction.InstructionEnum.nop_0)
                    complementTemplate.Add(MachineInstruction.InstructionEnum.nop_1);
                else if (instruction == MachineInstruction.InstructionEnum.nop_1)
                    complementTemplate.Add(MachineInstruction.InstructionEnum.nop_0);
                else
                    break;
            }

            // verify template
            if (complementTemplate.Count == 0)
            {
                ip++;
                return false;
            }

            // Find complement template ascending
            var complementIndex = 0;
            for (int i = ip + complementTemplate.Count + 1; i < soup.GetMax(); i++)
            {
                var instruction = soup.Get(i);

                if (instruction == complementTemplate[complementIndex])
                {
                    complementIndex++;
                    if (complementIndex == complementTemplate.Count)
                    {
                        ip = i;
                        return true;
                    }
                }
                else
                {
                    complementIndex = 0;
                }
            }

            // Nothing found
            ip++;
            return false;
        }

        private bool pop_dx(Creature creature)
        {
            ip++;
            if (st.Count == 0)
                return false;

            dx = st.Pop();
            return true;
        }

        private bool pop_cx(Creature creature)
        {
            ip++;
            if (st.Count == 0)
                return false;

            cx = st.Pop();
            return true;
        }

        private bool pop_bx(Creature creature)
        {
            ip++;
            if (st.Count == 0)
                return false;

            bx = st.Pop();
            return true;
        }

        private bool pop_ax(Creature creature)
        {
            ip++;
            if (st.Count == 0)
                return false;

            ax = st.Pop();
            return true;
        }

        private bool push_dx(Creature creature)
        {
            ip++;
            if (st.Count >= MaxStackCount)
                return false;
            st.Push(dx);
            return true;
        }

        private bool push_cx(Creature creature)
        {
            ip++;
            if (st.Count >= MaxStackCount)
                return false;
            st.Push(cx);
            return true;
        }

        private bool push_bx(Creature creature)
        {
            ip++;
            if (st.Count >= MaxStackCount)
                return false;
            st.Push(bx);
            return true;
        }

        private bool push_ax(Creature creature)
        {
            ip++;
            if (st.Count >= MaxStackCount)
                return false;
            st.Push(ax);
            return true;
        }

        private bool inc_c(Creature creature)
        {
            cx++;
            ip++;
            return true;
        }

        private bool dec_c(Creature creature)
        {
            cx--;
            ip++;
            return true;
        }

        private bool inc_b(Creature creature)
        {
            bx++;
            ip++;
            return true;
        }

        private bool inc_a(Creature creature)
        {
            ax++;
            ip++;
            return true;
        }

        private bool sub_ac(Creature creature)
        {
            ax = ax - cx;
            ip++;
            return true;
        }

        private bool sub_ab(Creature creature)
        {
            cx = ax - bx;
            ip++;
            return true;
        }

        private bool if_cz(Creature creature)
        {
            if (cx == 0)
                ip++;
            else
                ip += 2;
            return true;
        }

        private bool zero(Creature creature)
        {
            cx = 0;
            ip++;
            return true;
        }

        private bool sh1(Creature creature)
        {
            cx <<= 1;
            ip++;
            return true;
        }

        private bool or1(Creature creature)
        {
            cx ^= 1;
            ip++;
            return true;
        }

        private bool nop_1(Creature creature)
        {
            ip++;
            return true;
        }

        private bool nop_0(Creature creature)
        {
            ip++;
            return true;
        }
    }
}